/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package webapi;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.inspur.edp.lcm.metadata.api.IMdExtRuleContent;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.Metadata4RefDto;
import com.inspur.edp.lcm.metadata.api.entity.MetadataDto;
import com.inspur.edp.lcm.metadata.api.entity.MetadataFilter;
import com.inspur.edp.lcm.metadata.api.entity.MetadataHeader;
import com.inspur.edp.lcm.metadata.api.entity.MetadataIndexItemDto;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.api.entity.MetadataType;
import com.inspur.edp.lcm.metadata.api.entity.Page;
import com.inspur.edp.lcm.metadata.api.entity.ProcessMode;
import com.inspur.edp.lcm.metadata.api.entity.metadataindex.MetadataIndexDto;
import com.inspur.edp.lcm.metadata.api.service.MetadataProjectService;
import com.inspur.edp.lcm.metadata.api.service.MetadataService;
import com.inspur.edp.lcm.metadata.api.service.PackageGenerateService;
import com.inspur.edp.lcm.metadata.common.MetadataDtoConverter;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.common.configuration.MdExtRuleSerializerHelper;
import com.inspur.edp.lcm.metadata.devcommon.ManagerUtils;
import com.inspur.edp.lcm.metadata.spi.MdExtendRuleSerializer;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class MetadataServiceWebApi {
    private MetadataService metadataService;

    private MetadataService getMetadataService() {
        if (metadataService == null) {
            metadataService = SpringBeanUtils.getBean(MetadataService.class);
        }
        return metadataService;
    }

    private MetadataProjectService metadataProjectService;

    private MetadataProjectService getMetadataProjectService() {
        if (metadataProjectService == null) {
            metadataProjectService = SpringBeanUtils.getBean(MetadataProjectService.class);
        }
        return metadataProjectService;
    }

    PackageGenerateService packService;

    private PackageGenerateService getPackService() {
        if (packService == null) {
            packService = SpringBeanUtils.getBean(PackageGenerateService.class);
        }
        return packService;
    }

/******************************************************************************************/

    /**
     * 用于新建元数据之前初始化元数据实体
     *
     * @param nameSpace      元数据命名空间
     * @param code           元数据编号
     * @param name           元数据名称
     * @param type           元数据类型
     * @param bizObjectID    业务对象主键
     * @param metadataPath   元数据创建路径，相对于开发根路径的路径信息
     * @param extendProperty 扩展属性，默认为空，可以不赋值
     * @return
     */
    @GET
    @Path("initialized")
    public MetadataDto initializeMetadataEntity(@QueryParam(value = "nameSpace") String nameSpace,
        @QueryParam(value = "code") String code,
        @QueryParam(value = "name") String name,
        @QueryParam(value = "type") String type,
        @QueryParam(value = "bizObjectID") String bizObjectID,
        @QueryParam(value = "metadataPath") String metadataPath,
        @DefaultValue("") @QueryParam(value = "extendProperty") String extendProperty) {
        GspMetadata metadata = new GspMetadata();
        metadata.setHeader(new MetadataHeader());
        metadata.getHeader().setName(name);
        metadata.getHeader().setCode(code);
        metadata.getHeader().setNameSpace(nameSpace);
        metadata.getHeader().setType(type);
        metadata.getHeader().setBizObjectId(bizObjectID);
        metadata.setRelativePath(metadataPath);
        metadata.setExtendProperty(extendProperty);
        getMetadataService().initializeMetadataEntity(metadata);
        return MetadataDtoConverter.asDto(metadata);
    }

    /**
     * 新建元数据，元数据设计器打开之前文件被创建
     *
     * @param metadataDto 新建元数据需要的参数，新建元数据之前需要调用初始化元数据实体，因此该参数即为InitializeMetadataEntity返回的实体
     */
    @POST
    public void createMetadata(MetadataDto metadataDto) {
        String path = metadataDto.relativePath;
        getMetadataService().createMetadata(path, MetadataDtoConverter.asMetadata(metadataDto));
    }

    @DELETE
    @Path("/delete")
    public void deleteUri(@QueryParam(value = "fullPath") String fullPath) {
        fullPath = ManagerUtils.getAbsolutePath(fullPath);
        File file = new File(fullPath);
        if (file.isDirectory()) {
            getMetadataService().deleteFolder(fullPath);
        } else {
            getMetadataService().deleteMetadata(file.getParent(), file.getName());
        }
    }

    @PUT
    public void saveMetadata(MetadataDto metadataDto) {
        final GspMetadata metadata = MetadataDtoConverter.asMetadata(metadataDto);
        String fullPath = metadataDto.getRelativePath() + File.separator + metadataDto.getFileName();
        getMetadataService().saveMetadata(metadata, fullPath);
    }

    @GET
    @Path("/load")
    public MetadataDto loadMetadata(@QueryParam(value = "metadataFullPath") String metadataFullPath) {
        File file = new File(metadataFullPath);
        GspMetadata metadata = getMetadataService().loadMetadata(file.getName(), file.getParent());
        return MetadataDtoConverter.asDto(metadata);
    }

    @GET
    @Path("/metadataInBo")
    public MetadataDto loadMetadata(@QueryParam(value = "metadataFullPath") String metadataFullPath,
        @QueryParam(value = "currentProjPath") String currentProjPath) {
        GspMetadata metadata = getMetadataService().loadMetadataInBo(metadataFullPath, currentProjPath);
        return MetadataDtoConverter.asDto(metadata);
    }

    /**
     * @param spacePath        元数据路径
     * @param metadataTypeList 元数据类型
     * @return java.util.List<com.inspur.edp.lcm.metadata.api.entity.MetadataDto>
     * @author zhongchq
     **/
    @GET
    public List<MetadataDto> getMetadataList(@QueryParam(value = "path") String spacePath,
        @QueryParam(value = "metadataTypeList") String metadataTypeList) {
        List<GspMetadata> result = getMetadataService().getMetadataList(spacePath, handleMetadataTypeList(metadataTypeList));

        String projPath = getMetadataProjectService().getProjPath(spacePath);
        ProcessMode processMode = ProcessMode.GENERATION;
        if (projPath != null && !projPath.isEmpty()) {
            processMode = getMetadataProjectService().getProcessMode(projPath);
        }
        List<MetadataDto> metadataDtos = asMdDtos(result, processMode);
        metadataDtos.forEach(dto -> dto.setRelativePath(ManagerUtils.getRalativePath(dto.getRelativePath())));
        return metadataDtos;
    }

    /**
     * 根据当前工程路径获取bo下的元数据列表（会根据processMode过滤）
     *
     * @param path
     * @param metadataTypeList
     * @return List
     */
    @GET
    @Path(value = "/metadataListInBo")
    public List<MetadataDto> getMetadataListInBo(@QueryParam(value = "path") String path,
        @QueryParam(value = "metadataTypeList") String metadataTypeList) {
        List<MetadataDto> result = getMetadataService().getMetadataListInBo(path, handleMetadataTypeList(metadataTypeList));
        result.forEach(dto -> dto.setRelativePath(ManagerUtils.getRalativePath(dto.getRelativePath())));
        return result;
    }

    /**
     * 根据过滤条件获取元数据列表，如构件可细分为多种子构件，可根据filter精确过滤具体类型的元数据
     *
     * @param path     工程路径
     * @param typeCode 需要精确获取元数据类型的元数据编号
     * @param filter   过滤条件
     * @param postfix  要获取的元数据后缀，不传则默认获取工程下所有的元数据
     * @return
     */
    @GET
    @Path(value = "/filter")
    public List<MetadataDto> getMetadataByFilter(@QueryParam(value = "path") String path,
        @QueryParam(value = "typeCode") String typeCode, @QueryParam(value = "filter") String filter,
        @QueryParam(value = "postfix") String postfix) {
        MetadataFilter metadataFilter = new MetadataFilter();
        metadataFilter.setFilter(filter);
        metadataFilter.setTypeCode(typeCode);
        metadataFilter.setMetadataPostfix(postfix);
        List<GspMetadata> metadataByFilter = getMetadataService().getMetadataByFilter(path, metadataFilter);
        List<MetadataDto> dtoList = new ArrayList<>();
        if (metadataByFilter != null) {
            metadataByFilter.forEach(metadata -> {
                dtoList.add(MetadataDtoConverter.asDto(metadata));
            });
        }
        return dtoList;
    }

    /***
     * @author zhongchq
     * @param metadataPath
     * 元数据所在路径信息，当前工程下的某个路径，相对路径
     * @param metadataID
     *元数据唯一标识
     * @return com.inspur.edp.lcm.metadata.api.entity.MetadataDto
     **/
    @GET
    @Path(value = "/relied")
    public MetadataDto getRefMetadata(@QueryParam(value = "metadataPath") String metadataPath,
        @QueryParam(value = "metadataID") String metadataID) {
        return MetadataDtoConverter.asDto(getMetadataService().getRefMetadata(metadataPath, metadataID));
    }

    /**
     * 判断元数据新建的路径下是否有相同名称的文件存在
     *
     * @param path     元数据新建的路径
     * @param fileName 元数据文件名
     * @return
     */
    @GET
    @Path("/validation")
    public boolean validateRepeatName(@QueryParam(value = "path") String path,
        @QueryParam(value = "fileName") String fileName) {
        boolean canCreateFlag = getMetadataService().validateRepeatName(path, fileName);
        return canCreateFlag;
    }

    /***
     * 获取各元数据配置的元数据类型信息及后缀信息
     * @author zhongchq
     * @return java.util.List<com.inspur.edp.lcm.metadata.api.entity.MetadataType>
     **/
    @GET
    @Path(value = "/typeList")
    public List<MetadataType> getMetadataTypeList() {
        return getMetadataService().getMetadataTypeList();
    }

    @POST
    @Path(value = "/packages")
    public void GeneratePackage(@QueryParam(value = "projPath") String projPath) {
        getPackService().generatePackage(projPath);
    }

    /***
     * @author zhongchq
     * @param projectPath
     * 获取工程信息
     * @return com.inspur.edp.lcm.metadata.api.entity.MetadataProject
     **/
    @GET
    @Path(value = "/projectInfo")
    public MetadataProject getMetadataProjInfo(@QueryParam(value = "projectPath") String projectPath) {
        if (projectPath == null || projectPath.isEmpty()) {
            throw new IllegalArgumentException("projectPath参数不能为空");
        }
        return getMetadataService().getMetadataProjInfo(projectPath);
    }

    /***
     * @author zhongchq
     * @param scopeType 查找范围0.默认 1.本地 2.已安装 3.已引用 4.仓库中
     * @param text 查询内容
     * @param path 相对于开发根路径的路径信息
     * @param metadataTypeList 可以传递想要获取的元数据类型，类型用逗号隔开，如metadataTypeList=.be,.cmp,.api 该参数可以不赋值，默认获取所有类型
     * @param conflictAvoid 冲突避免
     * @param selectedPackageSource 包源
     * @param page 是否分页
     * @param pageSize 每页大小
     * @param pageIndex 第几页
     * @return com.inspur.edp.lcm.metadata.api.entity.metadataindex.MetadataIndexDto
     **/
    @GET
    @Path(value = "/mdlist")
    public MetadataIndexDto getMetadataIndexList(@QueryParam(value = "scopeType") int scopeType,
        @DefaultValue("") @QueryParam(value = "text") String text,
        @QueryParam(value = "path") String path,
        @QueryParam(value = "metadataTypeList") String metadataTypeList,
        @DefaultValue("") @QueryParam(value = "selectedPackageSource") String selectedPackageSource,
        @DefaultValue("true") @QueryParam(value = "page") boolean page,
        @DefaultValue("10") @QueryParam(value = "pageSize") int pageSize,
        @DefaultValue("0") @QueryParam(value = "pageIndex") int pageIndex,
        @DefaultValue("false") @QueryParam(value = "conflictAvoid") boolean conflictAvoid) {
        if (metadataTypeList != null) {
            metadataTypeList = metadataTypeList.toLowerCase();
        }
        MetadataIndexDto dto = new MetadataIndexDto();
        List<MetadataIndexItemDto> list;
        switch (scopeType) {
            // 离线
            case 1:
                list = getLocalMetadataList(text, path, metadataTypeList, page, pageSize, pageIndex);
                break;
            // 远程
            case 2:
                list = getRemoteMetadataIndexList(text, path, selectedPackageSource, metadataTypeList, page, pageSize, pageIndex, false, conflictAvoid);
                break;
            // 引用
            case 3:
                list = getRemoteMetadataIndexList(text, path, selectedPackageSource, metadataTypeList, page, pageSize, pageIndex, true, conflictAvoid);
                break;
            default:
                throw new IllegalArgumentException("Param scope is invalid.");
        }
        if (list.size() > 0) {
            dto.setMetadataIndexItems(list);
            Page page1 = new Page();
            page1.setPageIndex(pageIndex);
            page1.setPageSize(list.size());
            dto.setPage(page1);
        } else {
            Page page2 = new Page();
            page2.setPageIndex(pageIndex);
            page2.setPageSize(0);
            dto.setPage(page2);
        }
        return dto;
    }

    /**
     * @param scopeType        查找范围0.默认 1.本地 2.已安装 3.已引用 4.仓库中
     * @param currentPath      当前路径
     * @param metadataIndexDto MetadataIndexItemDto实体
     * @param isNeedMdDto      是否需要？？？
     * @return com.inspur.edp.lcm.metadata.api.entity.Metadata4RefDto
     * @author zhongchq
     **/
    @POST
    @Path(value = "pickmd")
    public Metadata4RefDto pickMetadataIndex(@QueryParam(value = "scopeType") int scopeType,
        @QueryParam(value = "currentPath") String currentPath,
        MetadataIndexItemDto metadataIndexDto,
        @DefaultValue("true") @QueryParam(value = "isNeedMdDto") boolean isNeedMdDto) {
        return getMetadataService().pickMetadataIndex(scopeType, currentPath, metadataIndexDto, isNeedMdDto);
    }

    @GET
    @Path(value = "/metadata")
    public MetadataDto loadMetadataByMetadataId(@QueryParam(value = "metadataId") String metadataId,
        @QueryParam(value = "path") String path) {
        final GspMetadata metadata = getMetadataService().loadMetadataByMetadataId(metadataId, path);
        Utils.checkNPE(metadata, "工程" + path + "中找不到元数据：" + metadataId);
        return MetadataDtoConverter.asDto(metadata);
    }

    @GET
    @Path(value = "/indexServerStatus")
    public String getIndexServerStatus(@QueryParam(value = "ip") String ip, @QueryParam(value = "port") String port) {
        return getMetadataService().getIndexServerStatus(ip, port);
    }

    @GET
    @Path(value = "/extendrule")
    public String getMdExtRule(@QueryParam(value = "metadataId") String metadataId,
        @QueryParam(value = "path") String path) {
        IMdExtRuleContent extendRule = getMetadataService().getMdExtRule(metadataId, path);
        String result = null;
        if (extendRule == null) {
            return null;
        }
        List<GspMetadata> metadataList = getMetadataService().getMetadataList(path);
        Optional<GspMetadata> optMetadata = metadataList.stream().filter(item -> item.getHeader().getId() == metadataId).findAny();
        if (!optMetadata.isPresent()) {
            throw new RuntimeException("不存在ID为" + metadataId + ",路径为" + path + "的元数据");
        }
        MdExtendRuleSerializer mdExtRulemanager = MdExtRuleSerializerHelper.getInstance().getManager(optMetadata.get().getHeader().getType());
        if (mdExtRulemanager != null) {
            JsonNode extRuleNode = mdExtRulemanager.serialize(extendRule);
            ObjectMapper objectMapper = Utils.getMapper();
            try {
                result = objectMapper.writeValueAsString(extRuleNode);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    @PUT
    @Path(value = "/extendrule")
    public void saveMdExtRule(@QueryParam(value = "metadataId") String metadataId,
        @QueryParam(value = "path") String path, String extendRuleStr) {
        List<GspMetadata> metadataList = getMetadataService().getMetadataList(path);
        Optional<GspMetadata> optMetadata = metadataList.stream().filter(item -> item.getHeader().getId() == metadataId).findAny();
        if (!optMetadata.isPresent()) {
            throw new RuntimeException("不存在ID为" + metadataId + ",路径为" + path + "的元数据");
        }
        MdExtendRuleSerializer mdExtRulemanager = MdExtRuleSerializerHelper.getInstance().getManager(optMetadata.get().getHeader().getType());
        IMdExtRuleContent extendRule = null;
        if (mdExtRulemanager != null) {
            ObjectMapper objectMapper = Utils.getMapper();
            try {
                JsonNode extRuleNode = objectMapper.readTree(extendRuleStr);
                extendRule = mdExtRulemanager.deSerialize(extRuleNode);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        getMetadataService().saveMdExtRule(metadataId, path, extendRule);
    }

    @GET
    @Path(value = "/processMode")
    public String getProcessMode(@QueryParam(value = "path") String path) {
        return getMetadataProjectService().getProcessMode(path).toString();
    }

    @GET
    @Path(value = "/mavensetting")
    public boolean getMavenSetting() {
        return true;
    }

    @GET
    @Path(value = "/projinpath")
    public List<String> getProjPathsInPath(@QueryParam(value = "path") String path) {
        return getMetadataProjectService().getProjPathsInPath(path);
    }

    @GET
    @Path(value = "/isMetadataRefed")
    public boolean isMetadataRefed(@QueryParam(value = "path") String path) {
        return getMetadataService().isMetadataRefed(path);
    }

    @GET
    @Path(value = "/mdpkgNameExist")
    public String getMdpkgNameExistInBo(@QueryParam(value = "path") String path,
        @QueryParam(value = "mdpkgName") String mdpkgName) {
        return getMetadataProjectService().getMdpkgNameExistInBo(path, mdpkgName);
    }

    private List<MetadataIndexItemDto> getRemoteMetadataIndexList(String text, String path,
        String selectedPackageSource,
        String metadataTypeList, boolean page, int pageSize,
        int pageIndex, boolean isFilterByRefs, boolean conflictAvoidFlag) {
        List<String> typeList = handleMetadataTypeList(metadataTypeList);
        return getMetadataService().getRemoteMetadataIndexList(text, path, selectedPackageSource, typeList, page,
            pageSize, pageIndex, isFilterByRefs, conflictAvoidFlag);
    }

    private List<MetadataIndexItemDto> getLocalMetadataList(String text, String path, String metadataTypeList,
        boolean page, int pageSize, int pageIndex) {
        List<String> typeList = handleMetadataTypeList(metadataTypeList);
        return getMetadataService().getLocalMetadataList(text, path, typeList, page, pageSize, pageIndex);
    }

    private List<MetadataDto> asMdDtos(List<GspMetadata> mds, ProcessMode processMode) {
        if (mds == null) {
            return null;
        }
        List<MetadataDto> list = new ArrayList<>();
        mds.forEach(md -> {
            MetadataDto metadataDto = MetadataDtoConverter.asDto(md);
            metadataDto.setProcessMode(processMode.toString());
            list.add(metadataDto);
        });
        return list;
    }

    private List<String> handleMetadataTypeList(String metadataTypes) {
        List<String> typeList = null;
        if (!(metadataTypes == null || metadataTypes.isEmpty())) {
            String[] types = metadataTypes.split(",");
            typeList = new ArrayList<>(Arrays.asList(types));
        }
        return typeList;
    }
}
